# coding=utf-8
from django import template
from django.apps import apps
from django.utils.safestring import mark_safe
from rest_framework.schemas import SchemaGenerator
from rest_framework.views import get_view_description
from django.conf import settings
from django.urls import reverse
from django.core.cache import caches, cache

register = template.Library()


class AppsApiNode(template.Node):
    def __init__(self, current_path):
        self.current_path = template.Variable(current_path)
        self.request = template.Variable('request')

    def get_all_apps(self):
        _apps = {}
        for app_conf in apps.get_app_configs():
            app_label = app_conf.label
            if app_label == 'rest_framework':
                continue
            app_key = "app:%s" % app_label
            app_m = {
                'app': app_label,
                'order': getattr(app_conf, 'order', 0),
                'icon': getattr(app_conf, 'icon', ''),
                'title': getattr(app_conf, 'verbose_name', app_label),
                'url': None,
                'active': False,
                'menus': []
            }
            _apps[app_key] = app_m
        return _apps


    def get_api_links(self, request):
        schema = SchemaGenerator()
        inspector = schema.endpoint_inspector_cls(schema.patterns, schema.urlconf)
        schema.endpoints = inspector.get_api_endpoints()
        links = {}
        for (path, method, callback) in schema.endpoints:
            view = schema.create_view(callback, method, None)
            path = schema.coerce_path(path, method, view)
            if not schema.has_view_permissions(path, method, view):
                continue
            if path.endswith("{format}"):
                continue
            link = view.schema.get_link(path, method, base_url=schema.url)
            
            if link.url in links:
                links[link.url]['actions'].append(link.action)
            else:
                links[link.url] = {
                    # 'name': name,
                    'fields': link.fields,
                    'url': link.url,
                    'actions': [link.action],
                    'description': link.description or get_view_description(callback, html=False),
                    'app': callback.__module__.split('.')[0]
                }
        return links
        

    def get_menus(self, request, current_path):
        
        _apps = cache.get("API_APPS", None)
        if not _apps:
            _apps = self.get_all_apps()
            cache.set("API_APPS", _apps)
        links = cache.get("API_LINKS", None)
        if not links:
            links = self.get_api_links(request)
            cache.set("API_LINKS", links)
        for path, link in links.items():
            para = {f.name: 'id' for f in link['fields'] if f.location == 'path'}
            try:
                _url = link['url'].format(**para)
            except :
                continue
            
            active = current_path.startswith(_url.rstrip('0/'))
            app_key = "app:%s" % link.pop('app')
            if app_key == "app:rest_framework":
                continue
            _apps[app_key]['menus'].append({
                'path': path,
                'title': link['description'].split('\n', 1)[0].replace("#", ""),
                'description': link['description'],
                'url': _url,
                'actions': link['actions'],
                'active': active,
            })
            if active and _apps[app_key]['active'] is False:
                _apps[app_key]['active'] = active
        for k in _apps.keys():
            _apps[k]['menus'].sort(key=lambda x: x['path'])
        return _apps.values()

    def render(self, context):
        _c_path = self.current_path.resolve(context)
        _request = self.request.resolve(context)
        menus = self.get_menus(_request, _c_path)
        html = '<nav class="sidbar bs-docs-sidebar"><ul class="nav bs-docs-sidenav">'
        html += '<li role="presentation"><a href="%s">首页</a></li>' % reverse("restdocs:index")
        for m in menus:
            if not m.get('menus', None):
                continue
            html += '<li class="{css}"><a href="#{app}" id=_{app}>{title}<span class="label label-default pull-right">{count}</span></a>'.format(
                css='active' if m['active'] else '',
                title=m['title'],
                count=len(m['menus']),
                app=m['app']
            )

            html += '<ul class="nav" >'
            for _sm in m.get('menus', []):
                actions = []
                for action in _sm['actions']:
                    if action == 'get':
                        actions.append('<span class="text-info">GET</span>')
                    if action == 'post':
                        actions.append('<span class="text-primary">POST</span>')
                    if action == 'put':
                        actions.append('<span class="text-warning">PUT</span>')
                    if action == 'patch':
                        actions.append('<span class="text-warning">PAT</span>')
                    if action == 'delete':
                        actions.append('<span class="text-danger">DEL</span>')

                html += '<li class="{css_class}"><a href="{url}">{title} <span class="action-tags pull-right">[{actions}]</span></a></li>'.format(
                    url=_sm['url'],
                    css_class='active' if _sm['active'] else '',
                    title=_sm['title'] or _sm['description'],
                    actions='&nbsp'.join(actions),
                )
            html += '</ul></li>'
        html += '</nav></ul>'
        return mark_safe(html)


@register.tag('apidocmenu')
def do_apimenu(parser, token):
    ag_name, path = token.split_contents()
    return AppsApiNode(path)


@register.inclusion_tag('rest_framework/apiheader.html', takes_context=True)
def apiheader(context):
    request = context['request']
    abrowser = request.META['HTTP_USER_AGENT'].find('7BYTE/ABrowser') >= 0
    context["abrowser"] = abrowser
    context["API_TITLE"] = getattr(settings, "API_TITLE", "API DOC")
    return context


@register.simple_tag()
def api_name():
    return getattr(settings, "API_TITLE", "API DOC")


@register.inclusion_tag('rest_framework/apifooter.html', takes_context=True)
def apifooter(context):
    site_short_title = getattr(settings, "API_SHORT_TITLE", "API DOC")
    licensed_to = getattr(settings, "API_LICENSED_TO", u"7byte.net")
    copyright = getattr(settings, "API_COPYRIGHT", u"成都拾贝科技有限公司(7byte.net)")
    return {
        'short_title': site_short_title,
        'licensed_to': licensed_to,
        'copyright': copyright,
    }
